/**
 * 
 */
package com.vision.core.cm.db.data;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import net.sf.gilead.pojo.gwt.LightEntity;


/**
 * 
 * @author Mark
 *
 */
@Entity
@Table(name = User.TABLE)
public class User extends LightEntity implements Serializable {
	
	public static interface FieldSpecs extends Serializable {
		int USERNAME_MAX = 20;
		int PASSWORD_MIN = 6;
		int PASSWORD_MAX = 60;
		int EMAIL_MAX = 100;
		int FIRST_NAME_MAX = 60;
		int MIDDLE_NAME_MAX = 60;
		int LAST_NAME_MAX = 60;
	}

	private static final long serialVersionUID = 2503959995676898241L;

	/* Table and Column names */
	public static final String TABLE = "users"; // NOTE Since "user" is a reserved word, we add an 's'
	public static final String COL_USERNAME = "username";
	public static final String COL_PASSWORD = "password";
	public static final String COL_EMAIL = "email";
	public static final String COL_FIRST_NAME = "firstname";
	public static final String COL_MIDDLE_NAME = "middlename";
	public static final String COL_LAST_NAME = "lastname";
	public static final String COL_STATUS = "status";
	public static final String COL_DATE_CREATED = "date_created";
	public static final String COL_LAST_LOGIN = "last_login";
	
	public static final String TABLE_USER_COMPANY = "user_company";
	public static final String COL_USER_USERNAME = "username";
	public static final String COL_COMPANY_ID = "company_id";
	
	public static final String TABLE_USER_ROLE = "user_role";
	public static final String COL_ROLE_NAME = "role_name";
	
	/**
	 * The name of the root user (superuser).
	 */
	public static final String ROOT_USERNAME = "root";
	
	/* Fields */
	@Id
	@Column(name = COL_USERNAME, updatable = false, length = FieldSpecs.USERNAME_MAX)
	private String username;
	
	@Column(name = COL_PASSWORD, nullable = false, length = FieldSpecs.PASSWORD_MAX)
	private String password;

	@Column(name = COL_EMAIL, nullable = false, unique = true, length = FieldSpecs.EMAIL_MAX)
	private String email;
	
	@Column(name = COL_FIRST_NAME, nullable = false, length = FieldSpecs.FIRST_NAME_MAX)
	private String firstName;
	
	@Column(name = COL_MIDDLE_NAME, length = FieldSpecs.MIDDLE_NAME_MAX)
	private String middleName;
	
	@Column(name = COL_LAST_NAME, nullable = false, length = FieldSpecs.LAST_NAME_MAX)
	private String lastName;
	
	@Column(name = COL_STATUS, nullable = false)
	@Enumerated(EnumType.STRING)
	private UserStatus status;
	
	@Column(name = COL_DATE_CREATED, nullable = false, updatable = false)
	private Timestamp dateCreated;
	
	@Column(name = COL_LAST_LOGIN)
	private Timestamp lastLogin;

	@ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@JoinTable(name = TABLE_USER_COMPANY,
			joinColumns = 			{ @JoinColumn(name = COL_USER_USERNAME, referencedColumnName = COL_USERNAME) },
			inverseJoinColumns = 	{ @JoinColumn(name = COL_COMPANY_ID, referencedColumnName = Company.COL_ID) }
			)
	private Set<Company> companies = new HashSet<Company>();
	
	@ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@JoinTable(name = TABLE_USER_ROLE,
			joinColumns = 			{ @JoinColumn(name = COL_USER_USERNAME, referencedColumnName = COL_USERNAME) },
			inverseJoinColumns = 	{ @JoinColumn(name = COL_ROLE_NAME, referencedColumnName = Role.COL_NAME) }
	)
	private Set<Role> roles = new HashSet<Role>();
	
	public User() { } // No-args constructor
	
	@Override
	public boolean equals(Object object) {
		if (object instanceof User) {
			User u = (User) object;
			return getUsername().equals(u.getUsername());
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		return getUsername() == null ? super.hashCode() : getUsername().hashCode();
	}
	
	@Override
	public String toString() {
		return getUsername() + " (" + getInverseName() + ")";
	}
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getMiddleName() {
		return middleName;
	}
	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
	public UserStatus getStatus() {
		return status;
	}
	public void setStatus(UserStatus status) {
		this.status = status;
	}

	public Timestamp getDateCreated() {
		return dateCreated;
	}
	public void setDateCreated(Timestamp dateCreated) {
		this.dateCreated = dateCreated;
	}

	public Timestamp getLastLogin() {
		return lastLogin;
	}
	public void setLastLogin(Timestamp lastLogin) {
		this.lastLogin = lastLogin;
	}

	public Set<Company> getCompanies() {
		return companies;
	}
	public void setCompanies(Set<Company> companies) {
		this.companies = companies;
	}
	
	public void addCompany(Company company) {
		if (companies == null)
			companies = new HashSet<Company>();
		companies.add(company);
	}
	
	public void addCompanies(List<Company> companiesToAdd) {
		getCompanies().addAll(companiesToAdd);
	}
	
	public void removeCompany(Company company) {
		if (companies != null)
			companies.remove(company);
	}
	
	public void removeCompanies(List<Company> companiesToRemove) {
		if (this.companies != null)
			this.companies.removeAll(companiesToRemove);
	}
 
	public Set<Role> getRoles() {
		return roles;
	}
 	
	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}
	
	public void addRole(Role role) {
		if (roles == null)
			roles = new HashSet<Role>();
		roles.add(role);
	}
	
	public void addRoles(List<Role> rolesToAdd) {
		if (roles == null)
			roles = new HashSet<Role>();
		roles.addAll(rolesToAdd);
	}
	
	public void removeRole(Role role) {
		if (roles != null)
			roles.remove(role);
	}
	
	public void removeRoles(List<Role> rolesToRemove) {
		if (this.roles != null)
			this.roles.removeAll(rolesToRemove);
	}
	
	// Helper Methods **********************************************************
	
	public String getInverseName() {
		StringBuilder sb = new StringBuilder(lastName + ", " + firstName);
		if (middleName != null && !middleName.isEmpty())
			sb.append(" " + middleName.charAt(0) + ".");
		return sb.toString();
	}
	
	/**
	 * Dummy setter for bean spec compliance. Does nothing.
	 * @param inverseName
	 */
	public void setInverseName(String inverseName) {
		// foo bar baz. We do nothing.
	}
	
	// Inner Classes ***********************************************************

	public enum UserStatus {
		// NOTE When adding new fields, add at the bottom
		ACTIVE,
		DEACTIVATED,
	}
	
}